/*
 * ClarkeCPE301DesignAssignment8.c
 *
 * Created: 5/7/2014 2:02:36 PM
 *  Author: clarkeb8
 */ 
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

volatile int sinWAVE = 0;										// Output value for DAC
volatile int phaseWAVE = 0;									// Phase of the sine wave
volatile int TICK = 0;												// Number of ticks that have occurred since the last transition
volatile int temp = 0;												// Value to the DAC before it has been transformed
volatile int WAIT = 20;											// When the timer interrupts the number of ticks to send.

static const int sinLOOKUP[] PROGMEM =			// Sine lookup table from http://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
{																		// Variables used: 235, 235, 32, Decimal
	118,121,124,127,130,133,136,139,142,146,149,152,155,158,160,163,166,169,172,175,177,180,183,185,188,190,193,195,197,200,202,204,
	206,208,210,212,214,216,217,219,221,222,223,225,226,227,228,229,230,231,232,232,233,234,234,234,235,235,235,235,235,235,235,234,
	234,233,233,232,231,231,230,229,228,227,225,224,223,221,220,218,217,215,213,211,209,207,205,203,201,199,196,194,192,189,187,184,
	181,179,176,173,170,168,165,162,159,156,153,150,147,144,141,138,135,132,128,125,122,119,116,113,110,107,103,100,97,94,91,88,
	85,82,79,76,73,70,67,65,62,59,56,54,51,48,46,43,41,39,36,34,32,30,28,26,24,22,20,18,17,15,14,12,
	11,10,8,7,6,5,4,4,3,2,2,1,1,0,0,0,0,0,0,0,1,1,1,2,3,3,4,5,6,7,8,9,
	10,12,13,14,16,18,19,21,23,25,27,29,31,33,35,38,40,42,45,47,50,52,55,58,60,63,66,69,72,75,77,80,
	83,86,89,93,96,99,102,105,108,111,114,118,
};

int iSQUAREDc(int i, int input)
{
	switch ( i )
	{
		case 1:					// i2c initialize
		TWBR = 0;		// Max speed
		TWSR = 0;
		break;
		case 2:					// i2c wait
		while(!(TWCR & (1<<TWINT)));
		return (TWSR&0xF8);
		break;
		case 3:					// i2c send
		TWDR = input; 									// Store the input
		TWCR = (1<<TWINT) | (1<<TWEN); 	// Enable i2c
		return iSQUAREDc(2, 0); 					// i2c wait
		break;
	}
	return 0;
}

void MCP4725(int i, int ms, int ls)
{
	switch ( i )
	{
		case 1:					// MCP start
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
		if(iSQUAREDc(2, 0)!=0x08) 		return;
		if(iSQUAREDc(3, 0xC0)!=0x18) 	return;
		break;
		case 2:					// MCP set
		if(iSQUAREDc(3, ms&0xF)!=0x28)	return;
		if(iSQUAREDc(3, ls)!=0x28)			return;
		break;
	}
}

void init()
{
	ADMUX = 0xD0;		// L-Justified, PC0 input, and external VRef with 10uf capacitor
	ADCSRA = 0x87;		// ADC enable with ck/128
	TIMSK1 = 0x06;		// ICIE1, OCIE1A << 1, TOIE1 << 0
	sei();							// Enable Interrupts
	OCR1A = 0x00FA;		// Delay
	TCCR1A = 0x00;		// CTC
	TCCR1B = 0x09;		// WGM12, CS10 << 1
	iSQUAREDc(1, 0);		// i2c initialize
	MCP4725(1, 0, 0);		// MCP start
}

int main(void)
{
	init();
	while(1)																// Output to MCP
	{
		cli();																// Read from global DAC
		int y = sinWAVE;
		MCP4725(2,(int)((y>>8)&0x0F),(int)(y&0xFF));	// Write to the MCP4725.
	}
}

ISR(TIMER1_COMPA_vect) 					// Timer 1 interrupt CTC
{
	cli();
	if(!(ADCSRA&(1<<ADSC)))					// Checks if the ADC is done
	{
		WAIT = 8+(ADC>>4);						// Sets the number of ticks to increment so the frequency varies.
		ADCSRA |= 1<<ADEN;					// Starts the ADC again.
		ADMUX = 0x40;
		ADCSRA |= 1<<ADSC;
	}
	int d = pgm_read_word(&sinLOOKUP[(phaseWAVE&1)?98-temp:temp]);	//Read from the inverse sine and adjust it with the phase.
	if(TICK+WAIT < d && TICK < d-WAIT)
	{
		TICK += WAIT;
	}
	else
	{
		TICK += WAIT;								// Increment the output state unless there is a chance to skip.
		do
		{
			TICK -= d;
			temp++;
			if(temp >= 99)							// Phase update
			{
				temp = 0;
				phaseWAVE = (phaseWAVE+1)&3;
			}
			d = pgm_read_word(&sinLOOKUP[(phaseWAVE&1)?98-temp:temp]);	// Sine wave inversion
		}
		while(TICK > d);
		int y = temp;
		switch(phaseWAVE)						// Scale and phase transformation of output
		{
			case 0: y += 99; break;
			case 1: y = 198-y; break;
			case 2: y = 99-y; break;
		}
		y <<= 4;
		sinWAVE = y;
	}
	sei();
}